package internal

import (
	"container/heap"
	"fmt"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"

	"GameServer/Common/data"
	"GameServer/Common/utils"
	"GameServer/Game/dbmodule"
	"GameServer/Game/lib"
	"GameServer/Game/m"
)

// 偷盗，规则
// 新手最大关是第一关，可以匹配别人，别人不匹配他。
// 玩家金币少于25W，移除匹配池。玩家金币少于25万，不被匹配。玩家金币多余25W，添加匹配池。
// 被偷玩家，5分钟内进入保护模式。
// 玩家被匹配，标记为使用。

// 匹配
// @todo 有性能问题
type MatchPool struct {
	rwSteal           *sync.RWMutex
	mStealMatch       map[string]string    // 偷盗匹配池
	mStealUse         map[string]string    // map[uida]uidb，玩家uida被玩家uidb当成老虎机玩家
	addStealProtec    chan *ProtectionUser // 偷盗添加保护用户
	removeStealProtec chan string          // 偷盗移除保护用户
	protecSteal       PriorityQueue        // 偷盗保护列表
	mStealProtect     map[string]bool      // 偷盗保护标记
}

type ProtectionUser struct {
	Uid     string // 受保护的玩家
	EndTime int64  // 受保护的结束时间
	index   int
}

type PriorityQueue []*ProtectionUser

func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
	return pq[i].EndTime < pq[j].EndTime
}
func (pq PriorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index = i
	pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*ProtectionUser)
	item.index = n
	*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.index = -1 // for safety
	*pq = old[0 : n-1]
	return item
}
func (pq *PriorityQueue) update(item *ProtectionUser, Uid string, EndTime int64) {
	item.Uid = Uid
	item.EndTime = EndTime
	heap.Fix(pq, item.index)
}

// 给玩家匹配老虎机玩家
func (mp *MatchPool) MatchStealUser(uid string) (ruid string, ok bool) {
	list := make([]*m.User, 0)
	mp.rwSteal.Lock()
	defer mp.rwSteal.Unlock()
	for key := range mp.mStealMatch {
		if key == uid { // 过滤掉自己
			continue
		}
		if _, tok := mp.mStealUse[key]; tok { // 玩家被使用
			continue
		}
		if _, tok := mp.mStealProtect[key]; tok { //玩家被保护
			continue
		}
		user := m.GetUser(key)
		list = append(list, user)
	}
	u := m.GetUser(uid)
	tu, err := lib.SloctMatchs(u.MaxPos.LockBlockTid, list) // 给玩家匹配老虎机玩家
	if err != nil {
		return "", false
	}
	// 被匹配的玩家，标记为使用
	mp.mStealUse[tu.Uid.Hex()] = u.Uid.Hex()
	return tu.Uid.Hex(), true
}

// 给玩家匹配老虎机玩家 匹配 lst中的玩家
func (mp *MatchPool) MatchStealUserLst(uid string, lst []string) (ruid string, ok bool) {
	list := make([]*m.User, 0)
	mp.rwSteal.Lock()
	defer mp.rwSteal.Unlock()
	for _, item := range lst {
		if item == uid { // 过滤掉自己
			continue
		}
		if _, tok := mp.mStealUse[item]; tok { // 玩家被使用
			continue
		}
		if _, tok := mp.mStealProtect[item]; tok { //玩家被保护
			continue
		}
		user := m.GetUser(item)
		list = append(list, user)
	}
	u := m.GetUser(uid)
	tu, err := lib.SloctMatchs(u.MaxPos.LockBlockTid, list) // 给玩家匹配老虎机玩家
	if err != nil {
		return "", false
	}
	// 被匹配的玩家，标记为使用
	mp.mStealUse[tu.Uid.Hex()] = u.Uid.Hex()
	return tu.Uid.Hex(), true
}

// 偷盗匹配池 移除玩家
func (mp *MatchPool) RemoveStealMatch(uid string) {
	mp.rwSteal.Lock()
	defer mp.rwSteal.Unlock()
	if _, ok := mp.mStealProtect[uid]; ok {
		return
	}
	delete(mp.mStealMatch, uid)
}

// 偷盗匹配池 添加玩家
func (mp *MatchPool) AddStealUser(uid string) {
	mp.rwSteal.Lock()
	defer mp.rwSteal.Unlock()
	mp.mStealMatch[uid] = uid
}

// 偷盗匹配池 移除已经使用
func (mp *MatchPool) RemoveStealUse(uid string) {
	mp.rwSteal.Lock()
	defer mp.rwSteal.Unlock()
	delete(mp.mStealUse, uid)
}

// 偷盗匹配池 是否已经使用
func (mp *MatchPool) StealIsUse(uid string) (r string, ok bool) {
	mp.rwSteal.Lock()
	defer mp.rwSteal.Unlock()
	r, ok = mp.mStealUse[uid]
	return
}

// 偷盗匹配池 添加保护玩家
func (mp *MatchPool) AddStealProtec(uid string, endTime int64) {
	mp.mStealProtect[uid] = true
	mp.addStealProtec <- &ProtectionUser{Uid: uid, EndTime: endTime}
}

// 偷盗匹配池 移除受保护的玩家
func (mp *MatchPool) RemoveStealProtec(uid string) {
	if _, ok := mp.mStealProtect[uid]; !ok {
		return
	}
	mp.removeStealProtec <- uid
}

// 偷盗匹配池 移除受保护的玩家
func (mp *MatchPool) protectionsStealRemove() {
	// item := heap.Pop(&mp.protecSteal).(*ProtectionUser)
	// delete(mp.mStealProtect, item.Uid)
}

func (mp *MatchPool) Init() {
	mp.rwSteal = new(sync.RWMutex)
	mp.mStealMatch = make(map[string]string)
	mp.mStealUse = make(map[string]string)
	mp.protecSteal = make(PriorityQueue, 0)
	mp.addStealProtec = make(chan *ProtectionUser, 1000)
	mp.removeStealProtec = make(chan string, 1000)
	mp.mStealProtect = make(map[string]bool)

	luser := make([]*m.User, 0)
	// err := dbmodule.MgoDB.GameDB.FindAll("user", bson.M{"gold": bson.M{"$gte": data.SlotLimitGold}}, bson.M{"_id": 1}, &objIds)
	err := dbmodule.MgoDB.GameDB.FindAll("user", nil, nil, &luser)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Error("匹配池查找数据库失败")
		return
	}
	for _, item := range luser {
		uid := item.Uid.Hex()
		if item.Gold >= data.SlotLimitGold && item.MaxPos.LockBlockTid > 1 { //玩家金币大于25W，进入偷盗匹配池
			mp.mStealMatch[uid] = uid
		}
	}
}

func (mp *MatchPool) Run(closeSig chan bool) {
	now := utils.TNow().Local()
	for {
		var effective time.Time
		if mp.protecSteal.Len() == 0 {
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = time.Unix(mp.protecSteal[0].EndTime, 0)
		}

		select {
		case <-closeSig:
			return
		case now = <-time.After(effective.Sub(now)):
			for mp.protecSteal.Len() > 0 {
				EndTime := mp.protecSteal[0].EndTime
				if EndTime > effective.Unix() {
					break
				}
				// mp.protectionsStealRemove()
				item := heap.Pop(&mp.protecSteal).(*ProtectionUser)
				delete(mp.mStealProtect, item.Uid)
				fmt.Println(item.EndTime)
			}
			continue
		case addPro := <-mp.addStealProtec:
			heap.Push(&mp.protecSteal, addPro)
			// case delProUid := <-mp.removeStealProtec:
			// 	mp.protectionsStealRemove(delProUid)
		}
		now = utils.TNow().Local()
	}
}

func (mp *MatchPool) Destroy() {

}
